import com.aliasi.classify.*;
import com.aliasi.lm.*;
import com.aliasi.stats.MultivariateDistribution;
import com.aliasi.tokenizer.*;
import com.aliasi.util.AbstractExternalizable;
import java.io.*;
import java.util.*;

public class CalibratedLMClassifier<L extends LanguageModel.Dynamic> extends DynamicLMClassifier<L> {

 private DynamicLMClassifier<L> lmc = null;
 
 public CalibratedLMClassifier ( DynamicLMClassifier<L> lmc ) { 
	super(lmc.categories(), (L[])(languageModels(lmc)) ); 
 	this.lmc = lmc;
 }

 public void compileTo(ObjectOutput objOut) throws IOException {
	 objOut.writeObject(new Externalizer<L>(this));
 }

 public static CalibratedLMClassifier<NGramBoundaryLM> createNGramBoundary(String[] categories, int maxCharNGram) {
   return new CalibratedLMClassifier(DynamicLMClassifier.createNGramBoundary(categories,maxCharNGram));
 }
 
 public static CalibratedLMClassifier<NGramProcessLM> createNGramProcess(String[] categories, int maxCharNGram) {
   return new CalibratedLMClassifier(DynamicLMClassifier.createNGramProcess(categories,maxCharNGram));
 }

 public static CalibratedLMClassifier<TokenizedLM> createTokenized(String[] categories, TokenizerFactory tokenizerFactory, int maxTokenNGram) {
   return new CalibratedLMClassifier(DynamicLMClassifier.createTokenized(categories,tokenizerFactory,maxTokenNGram));
 } 
 
 public void handle(Classified<CharSequence> classified) {
   lmc.handle(classified);
 }

 public void resetCategory(String category, L lm, int newCount) {
   lmc.resetCategory(category, lm, newCount);
 }

 public void train(String category, CharSequence sampleCSeq, int count) {
   lmc.train(category, sampleCSeq, count);
 }

 public String[] categories() {
   return lmc.categories();
 } 

 public com.aliasi.stats.MultivariateEstimator categoryDistribution() {
   return lmc.categoryDistribution();
 }

 public MyJointClassification classify(CharSequence cSeq) {
   return MyJointClassification.calibrate(lmc.classify(cSeq));
 }

 public MyJointClassification classifyJoint (char[] cs, int start, int end) {
   return MyJointClassification.calibrate(lmc.classifyJoint(cs,start,end));
 }

 public L languageModel(String category) {
   return lmc.languageModel(category);
 }

 public L[] languageModels() {
   return (L[])(languageModels(lmc));
 }

 private static LanguageModel.Dynamic[] languageModels ( DynamicLMClassifier lmc ) {
	String[] categories = lmc.categories();
	List<LanguageModel.Dynamic> aux = new ArrayList<LanguageModel.Dynamic>();
	for ( String cat : categories ) aux.add( (LanguageModel.Dynamic)(lmc.languageModel(cat)) );
	return (LanguageModel.Dynamic[])(aux.toArray(new LanguageModel.Dynamic[0]));
 }
 
 static class Externalizer<LL extends LanguageModel.Dynamic> extends AbstractExternalizable {

 static final long serialVersionUID = -5411956637253735953L;
 final CalibratedLMClassifier<LL> mClassifier;
 public Externalizer() {
     mClassifier = null;
 }
 public Externalizer(CalibratedLMClassifier<LL> classifier) {
     mClassifier = classifier;
 }
 @Override
 public void writeExternal(ObjectOutput objOut) throws IOException {
     objOut.writeObject(mClassifier.categories());
     mClassifier.categoryDistribution().compileTo(objOut);
     int numCategories = mClassifier.categories().length;
     for (int i = 0; i < numCategories; ++i) ((LanguageModel.Dynamic) mClassifier.languageModel( mClassifier.categories()[i]) ).compileTo(objOut);
 }
 @Override
 public Object read(ObjectInput objIn) throws ClassNotFoundException, IOException {
     String[] categories = (String[]) objIn.readObject();
     MultivariateDistribution categoryEstimator = (MultivariateDistribution) objIn.readObject();
     LanguageModel[] models = new LanguageModel[categories.length];
     for (int i = 0; i < models.length; ++i) models[i] = (LanguageModel) objIn.readObject();
     return new CalibratedLMClassifier2(categories,models,categoryEstimator);
 }

 class CalibratedLMClassifier2 extends LMClassifier {
	  public CalibratedLMClassifier2(String[] categories, LanguageModel[] languageModels, MultivariateDistribution categoryDistribution) {
		 super(categories,languageModels,categoryDistribution);
	  }
	  public JointClassification classify(CharSequence cSeq) {
		   return MyJointClassification.calibrate(super.classify(cSeq));
	  }
	  public JointClassification classifyJoint (char[] cs, int start, int end) {
		   return MyJointClassification.calibrate(super.classifyJoint(cs,start,end));
	  }
  }
}


}
